/**
 * @file mYmodem.c
 * @author ZMC (czm@comtika.com)
 * @brief 
 * @version 1.0.0.0
 * @date 2019-03-07
 * 
 * 
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "cm_sys.h"
#include "cm_time.h"
#include "mYmodem.h"

//#include "bsp_uart.h"
//#include "uart_api.h"

#include "platform.h"



#define mYmodem_get_max_size() cm_ota_get_capacity()

#define SOH (0x01) /* start of 128-byte data packet */
#define STX (0x02) /* start of 1024-byte data packet */
#define EOT (0x04) /* end of transmission */
#define ACK (0x06) /* acknowledge */
#define NAK (0x15) /* negative acknowledge */
#define CA (0x18)  /* two of these in succession aborts transfer */
#define ABORT1 (0x41) /* 'A' == 0x41, abort by user */
#define ABORT2 (0x61) /* 'a' == 0x61, abort by user */
#define YMODEM_SEND_1K  //transmit file packet size
#define MAX_ERRORS (120) // 60s

typedef struct{
	uint8_t head;
	uint8_t packet_num;
	uint8_t packet_num_r;
	uint8_t data[];
}mYmodem_frame_t;

static void(*mYmodem_send)(uint8_t*data,uint16_t len)=0;
static uint32_t file_size;
static uint16_t Cal_CRC16(const uint8_t *data, uint32_t size);

/**
 * @brief 文件传输完成后调用获得文件大小
 * 
 * @return uint32_t 
 */
uint32_t mYmodem_get_file_size(void)
{
	return file_size;
}

/**
 * @brief 
 * 
 * @param buf 
 * @param len 
 * @return uint16_t 0:failed, 128 || 1024 success
 */
static uint16_t mYmodem_packet_check(mYmodem_frame_t* buf, uint16_t len)
{
	uint16_t crc16_cal,crc16_packet,payload_len = 0;

	payload_len = (buf->head == SOH) ? 128 : 1024;
	if(buf->packet_num + buf->packet_num_r != 0xFF){
		return 0;
	}
	crc16_cal = Cal_CRC16(buf->data, payload_len);
	crc16_packet = buf->data[payload_len] << 8 | buf->data[payload_len + 1];
	return (crc16_cal == crc16_packet) ? payload_len : 0;
}

mYmodem_handle_t hmYmodem;
void mYmodem_init(void (*psend)(uint8_t*data,uint16_t len))
{
	if(psend)mYmodem_send = psend;
	memset(&hmYmodem, 0, sizeof(mYmodem_handle_t));
}



/**
 * @brief
 * 
 * @return int8_t -1 failed, file size too large.
 * 				  -2 failed, abort by sender.
 * 				  -3 failed, max errors reached.
 * 		   
 * 				  0 receive process ongoing. 
 * 				  1 receive success.
 */
int32_t mYmodem_client(void)
{
	if(get_runtime( ) - hmYmodem.rec_time > 1000 || hmYmodem.error){
		hmYmodem.error = 0;
		hmYmodem.rec_time = get_runtime( );
		hmYmodem.errors++;

		if(hmYmodem.errors > MAX_ERRORS) {
			mYmodem_init(NULL);
			hmYmodem.result = -3;
		}

		hmYmodem.txbuf[0] = (hmYmodem.packet_to_rec) ? NAK : 'C';
		hmYmodem.txlen = 1;
	}

	if(hmYmodem.txlen){
		mYmodem_send(hmYmodem.txbuf,hmYmodem.txlen);
		hmYmodem.txlen = 0;
	}

	return hmYmodem.result;
}
	

/**
 * @brief
 * 
 * @return int8_t -1 failed, file size too large.
 * 				  -2 failed, abort by sender.
 * 				  -3 failed, max errors reached.
 * 		   
 * 				  0 receive process ongoing. 
 * 				  1 receive success.
 */
void mYmodem_recv(uint8_t *data, uint16_t len)
{
	uint16_t payload_len;
	uint8_t  txbuf[8];			//接收文件最多回复2-3字节
	uint8_t  ret = 0, txlen = 0, error = 0;

	hmYmodem.rec_time = get_runtime( );
	mYmodem_frame_t* buf = (mYmodem_frame_t*)data;
		
	switch(buf->head)
	{
	case STX:case SOH:
		payload_len = mYmodem_packet_check(buf, len);
		if(!payload_len) {			
			error = 1;
			break;
		}
		if(hmYmodem.packet_to_rec == 0) {	//文件名和文件大小
			if (buf->packet_num == 0){
				uint16_t i=0;
				for(i=0;i<len;i++){
					if(isprint(buf->data[i])){
						break;
					}
				}
				char*p = strstr((char*)(&buf->data[i]),".bin");
				for(;i<len;i++){
					if(buf->data[i]=='\0'){
						break;
					}
				}
				i++;
				sscanf((char*)(&(buf->data[i])),"%d",(int*)&file_size);


#if 0   //ing
				cm_ota_set_otasize(file_size);

				// file_size = mYmodem_find_fileSize(buf,payload_len);
				if(file_size > mYmodem_get_max_size() || p == NULL){
					txbuf[0] = CA;
					txbuf[1] = CA;
					txlen = 2;
					ret = -1;
					break;
				}

#endif



				hmYmodem.packet_to_rec = 1;
				txbuf[0] = ACK;
				txbuf[1] = 'C';
				txlen = 2;
			}else{
				error = 1;
				break;
			}
		}else{												
			if((hmYmodem.eot_num == 0) && ((hmYmodem.packet_to_rec & 0xFF) == buf->packet_num)){	//文件内容
				//uint32_t addr = init.file_save_addr + (buf->packet_num - 1)*payload_len;
				// init.flash_erase_1K(addr);
				// init.flash_program_1K(addr,buf->data,payload_len);

#if 0   //ing
				int32_t ret = cm_ota_firmware_write((char*)buf->data,payload_len);
#endif

				cm_log_printf(0,"packet %d, ret=%d\n",buf->packet_num,ret);
				hmYmodem.packet_to_rec++;
				txbuf[0] = ACK;
				txlen = 1;
			}else if ((hmYmodem.eot_num == 2) && buf->packet_num == 0){					//文件传输完成
				txbuf[0] = ACK;	
				txlen = 1;
				ret = 1;
			}else{
				error = 1;
				break;
			}
		}
		break;

	case EOT:
		hmYmodem.eot_num++;
		if(hmYmodem.eot_num == 1){
			txbuf[0] = NAK;
			txlen = 1;
		}else{
			txbuf[0] = ACK;
			txbuf[1] = 'C';
			txlen = 2;
		}
		break;
	
	case CA:case 'A':case 'a':
		ret = -2;
		break;
	
	default:
		error = 1;
		break;
	}

	if(txlen)
	{
		memcpy(hmYmodem.txbuf, txbuf, txlen);
		hmYmodem.txlen = txlen;
	}

	hmYmodem.error = error;

	hmYmodem.result = ret;
}


/*--------------------------------CRC for Ymodem----------------------------------------*/

/**
 * @brief  Update CRC16 for input byte
 * @param  CRC input value
 * @param  input byte
 * @retval Updated CRC value
 */
static uint16_t UpdateCRC16(uint16_t crcIn, uint8_t byte)
{
	uint32_t crc = crcIn;
	uint32_t in = byte | 0x100;
	do
	{
		crc <<= 1;
		in <<= 1;

		if (in & 0x100)
			++crc;

		if (crc & 0x10000)
			crc ^= 0x1021;

	} while (!(in & 0x10000));
	return (crc & 0xffffu);
}

/**
 * @brief  Cal CRC16 for YModem Packet
 * @param  data
 * @param  length
 * @retval CRC value
 */
static uint16_t Cal_CRC16(const uint8_t *data, uint32_t size)
{
	uint32_t crc = 0;
	const uint8_t *dataEnd = data + size;

	while (data < dataEnd)
	{
		crc = UpdateCRC16(crc, *data++);
	}
	crc = UpdateCRC16(crc, 0);
	crc = UpdateCRC16(crc, 0);

	return (crc & 0xffffu);
}



// /*--------------------------------Ymodem Send file---------------------------------------*/
// /*重要函数：

// 将flash中的文件发送出去,文件大小一般不超过4M,文件名称长度不超过20字符

// 返回值：1：文件发送成功；0文件正在发送中；-1文件发送失败*/
// int8_t YmodemTransmitFile(uint32_t file_addr, void (*p_send)(uint8_t *buf, uint16_t len), uint8_t showinfo)
// {
// 	g_timer_t current_time = g_timer_ms;
// 	static g_timer_t time;
// 	static uint32_t ptr;
// 	static int32_t packet_num, filesize;
// 	static uint8_t errors;
// 	uint16_t rxcnt = 0;
// 	volatile int8_t ret = 0;
// 	uint8_t y_txbuf[6];

// 	if (current_time - time < 5000)
// 	{
// 		if (YmodemFrameReceived(&rxcnt))
// 		{
// 			if (rxcnt == 1 && y_rxbuf[0] == 'C') //收到的信息是‘C’,发送文件名称和文件大小
// 			{
// 				/*send name and size*/
// 				filesize = 0;

// 				ptr = file_addr; //指针指向文件起始内容

// 				if (!filesize || filesize > FLASH_OTA_BLOCK_SIZE)
// 				{
// 					if (showinfo)
// 						printf("Can not find any file!\r\n");
// 					ret = -1;
// 				}
// 			}
// 			else if (rxcnt == 2 && y_rxbuf[0] == ACK && y_rxbuf[1] == 'C') //收到的信息是ACK和‘C’，发送packet 1或者发送全0包数据
// 			{
// 				if (filesize)
// 					packet_num = 1;
// 				else
// 					packet_num = 0;

// 				YmodemSendPacket(ptr, packet_num, &filesize, p_send);
// 			}
// 			else if (rxcnt == 1)
// 			{
// 				if (y_rxbuf[0] == ACK) //收到ACK，发送后续数据包，无数据时发送EOT
// 				{
// 					if (packet_num)
// 					{
// 						packet_num++;
// 						YmodemSendPacket(ptr, packet_num, &filesize, p_send);
// 					}
// 					else
// 					{
// 						if (showinfo)
// 							printf("File transfer success!\r\n");
// 						errors = 0;
// 						ret = 1;
// 					}
// 				}
// 				else if (y_rxbuf[0] == NAK) //收到NAK，正常时发送文件结束符号EOT，不正常时重发数据包
// 				{
// 					if (!filesize)
// 					{
// 						y_txbuf[0] = EOT;
// 						p_send(y_txbuf, 1);
// 					}
// 					else
// 					{
// 						errors++;
// 						if (errors < 5)
// 						{
// 							YmodemSendPacket(ptr, packet_num, &filesize, p_send); //重发数据
// 						}
// 						else
// 						{
// 							ret = -1;
// 						}
// 					}
// 				}
// 			}
// 			time = current_time; //更新发送时间
// 			YmodemFlushRxbuf();
// 		}
// 	}
// 	else
// 	{
// 		errors++;
// 		time = current_time;
// 	}

// 	if (errors > 5)
// 	{
// 		y_txbuf[0] = CA;
// 		y_txbuf[1] = CA;
// 		p_send(y_txbuf, 2);
// 		if (showinfo)
// 			printf("File transfer failed!\r\n");
// 		ret = -1;
// 	}

// 	if (ret == -1)
// 	{
// 		errors = 0;
// 		packet_num = 0;
// 	}
// 	return ret;
// }

// static void YmodemSendFileNameSize(uint32_t addr, int32_t *size, void (*p_send)(uint8_t *buf, uint16_t len))
// {
// 	uint16_t i = 0, j, k, crc;
// 	uint8_t buf[256];

// 	spi_flash_quad_io_read(addr, buf, 256);

// 	y_rxbuf[i++] = SOH;
// 	y_rxbuf[i++] = 0;
// 	y_rxbuf[i++] = ~y_rxbuf[1];
// 	/*file name*/

// 	for (k = 0; i < 128 + 3; i++)
// 		y_rxbuf[i] = buf[k++];

// 	crc = Cal_CRC16(&y_rxbuf[3], i - 3);
// 	y_rxbuf[i++] = crc >> 8;
// 	y_rxbuf[i++] = crc;
// 	p_send(y_rxbuf, i);

// 	for (i = 3; i < 128 + 3; i++)
// 	{
// 		if (y_rxbuf[i] == '\0')
// 		{
// 			i++;
// 			break;
// 		}
// 	}

// 	for (j = i; j < 128 + 3; j++)
// 	{
// 		if (y_rxbuf[j] == ' ' || y_rxbuf[j] == '\0')
// 		{
// 			y_rxbuf[j] = '\0';
// 			break;
// 		}
// 	}

// 	*size = atoi((char *)&y_rxbuf[i]);
// }

// /*添加校验码并发送,注意：buf是帧第一个字节！cnt不包括crc*/
// static void YmodemSendPacket(uint32_t addr, uint16_t packet_num, int32_t *cnt, void (*p_send)(uint8_t *buf, uint16_t len))
// {
// 	uint16_t crc, i = 0;
// 	uint32_t new_ptr;
// 	int32_t new_cnt;

// #ifdef YMODEM_1K
// #define PACKET_SIZE 1024
// #define FRAME_HEAD STX
// #else
// #define PACKET_SIZE 128
// #define FRAME_HEAD SOH
// #endif

// 	if (packet_num)
// 	{
// 		new_ptr = addr + (packet_num - 1) * PACKET_SIZE;
// 		new_cnt = *cnt - (packet_num - 1) * PACKET_SIZE;

// 		if (new_cnt > 0)
// 		{
// 			y_rxbuf[i++] = FRAME_HEAD;
// 			y_rxbuf[i++] = packet_num;
// 			y_rxbuf[i++] = ~packet_num;

// #ifdef YMODEM_1K
// 			if (new_cnt >= 1024)
// 			{
// 				spi_flash_quad_io_read(new_ptr, &y_rxbuf[3], 1024);
// 				i = 1024 + 3;
// 			}
// 			else if (new_cnt > 128)
// 			{
// 				spi_flash_quad_io_read(new_ptr, &y_rxbuf[3], new_cnt);

// 				// for(;i<new_cnt + 3;i++)
// 				// 	y_rxbuf[i] = GET_FLASH_BYTE(new_ptr++);

// 				for (i = 3 + new_cnt; i < 1024 + 3; i++)
// 					y_rxbuf[i] = 0;
// 			}
// 			else
// 			{
// 				y_rxbuf[0] = SOH;
// 				spi_flash_quad_io_read(new_ptr, &y_rxbuf[3], new_cnt);
// 				// for(;i<new_cnt + 3;i++)
// 				// 	y_rxbuf[i] = GET_FLASH_BYTE(new_ptr++);

// 				for (i = 3 + new_cnt; i < 128 + 3; i++)
// 					y_rxbuf[i] = 0;
// 			}
// #else
// 			if (new_cnt >= 128)
// 			{
// 				for (; i < +3128; i++)
// 					y_rxbuf[i] = GET_FLASH_BYTE(new_ptr++);
// 			}
// 			else
// 			{
// 				for (; i < new_cnt + 3; i++)
// 					y_rxbuf[i] = GET_FLASH_BYTE(new_ptr++);

// 				for (; i < 128 + 3; i++)
// 					y_rxbuf[i] = 0;
// 			}
// #endif
// 		}
// 		else //文件数据发送完毕，将待发送文件置0,并发送文件发送完毕符号
// 		{
// 			*cnt = 0;
// 			y_rxbuf[0] = EOT;
// 			p_send(y_rxbuf, 1);
// 			return;
// 		}
// 	}
// 	else //发送全0数据包
// 	{
// 		i = 0;
// 		y_rxbuf[i++] = SOH;
// 		y_rxbuf[i++] = 0;
// 		y_rxbuf[i++] = ~y_rxbuf[1];
// 		for (; i < 128 + 3; i++)
// 			y_rxbuf[i] = 0;
// 	}

// 	crc = Cal_CRC16(&y_rxbuf[3], i - 3);
// 	y_rxbuf[i++] = crc >> 8;
// 	y_rxbuf[i++] = crc;
// 	p_send(y_rxbuf, i);
// }